home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Practical Algorithms for Image Analysis
/
Practical Algorithms for Image Analysis.iso
/
TARFILE.GZ
/
tarfile
/
ch_4.3
/
xcc
/
xcc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-09-11
|
19KB
|
774 lines
/*
* xcc.h
*
* Practical Algorithms for Image Analysis
*
* Copyright (c) 1997, 1998, 1999 MLMSoftwareGroup, LLC
*/
/*
* XC(enter)C(ircular disks)
*
* determine, in an efficient manner, (estimates of) center positions
* for circular objects by taking advantage of knwoledge about the
* circular object shape and object size, the latter in the form of
* a known radius, R, or mean and variance of the distribution of radii:
*
* speed enhancements derive from two principal sources:
* -- i) the (vertical) interval, DELTA_i, of the raster scan performed
* to locate object boundaries is set in accordance with
* the value supplied for D == 2*R (or <D>) so as to sample
* each object in at least n (and at most n+1) successive scans:
* DELTA_i is thus simply given as D/n; n may be adjusted to reflect
* the fidelity of the object (``disk'') contour: for a monodisperse
* distribution of perfect binary disks n might be set to 2 for
* greatest gain in computatinal efficiency, while a noisy contour
* might necessitate a higher sampling frequency, or greater n, to
* ensure a better estimate of the center position, derived from a
* set of 2*n (maximally 2*(n+1)) contour points;
*
* --ii) assuming circular shapes, the center position is determined
* from a set of boundary points, implying an associated complexity
* O(N), N denoting the number of object pixels, as opposed to the
* O(N**2) dependence of template matching methods
*
*/
#include "xcc.h"
#define TESTSET 100 /* test data set containing chords */
#define TOTAL_DISK TESTSET
#undef DEBUG
#undef DPRINT
#undef DBG_MEM
#undef SHOW_ETLL
#undef CHECK_ETLL
#undef CHECK_DLL
#define NO_DISPLAY -1
#define TERSE 0
#define VERBOSE 1
#define DISPL_PAGE 1
#define RESET 1
#define NO_RESET 0
#define BORDER_WIDTH 1 /* zero border width (in pix), default value */
#define EDGE_FILTER_LEN 5 /* default leng of 1d edge detection filter */
#define NCH_MIN 2 /* min nof scan lines (chords) per disk */
#define CONTOUR_COLOR 127
#define UPDATE_DL 6 /* update disk dia, based on _DL proc disks */
#define OFF 0
#define ON 1
#define F_TO_INT(a) ( ((a)-(int)(a)>0.5) ? ((int)(a)+1) : ((int)(a)) )
/*
* global variables
*/
extern char *optarg;
extern int optind, opterr;
int WRITE_FILE = 0;
int DISP_MODE = TERSE; /* VERBOSE, TERSE or NO_DISPLAY */
int TEST_INPUT = 0;
int SCAN_IMG = 1;
int DISPL_DISK_BDY = 1; /* when set, display convex hull */
int ZB = -1;
int R_DSK = -1; /* disk radius estimate supplied ? */
int VERT_SI = -1; /* vertical scan interval supplied ? */
char *IMG_TYPE = "B"; /* img type: B or G */
int GRAY_LEV = 0; /* BNRY(0)/GRTN(1) */
int UPDATE_DDIA = 1; /* when set, update disk dia */
int INP_FILE_TYPE = -1; /* input file types */
int TPL = 0;
FILE *fpIn, *fpOut;
int del_ir; /* vertical scan interval */
float drad, ddia; /* estimated disk radius, diameter */
/*
* display disk contours
*/
void
displ_disk_contours (struct linklist *list, Image * imgIO, int value)
{
struct disk *cdsk;
int nd;
llhead (list);
if ((nd = ll_length (list)) != 0) { /* list not empty */
#ifdef DPRINT
gprintf (fpOut, "\n...display disk contours\n");
#endif
do {
if ((cdsk = (struct disk *) list->clp->item)->Type == Accept) {
draw_circle (cdsk->ctr.x, cdsk->ctr.y, cdsk->rad, imgIO, value);
}
} while (llnext (list) == True);
}
#ifdef DPRINT
else
gprintf (fpOut, " list empty\n");
#endif
}
/*
* initialize disk list
*
* note: matching function to be set by call to llsetmatch() in code as needed
*/
void
init_dll (struct linklist *disk_list)
{
llzero (disk_list); /* init empty list */
llsetsize (sizeof (struct disk), disk_list);
}
/*
* initialize edge_tuple list
*
* note: matching function to be set by call to llsetmatch() in code as needed
*/
void
init_etll (struct linklist *edge_tuple_list)
{
llzero (edge_tuple_list); /* init empty list */
llsetsize (sizeof (struct edge_tuple), edge_tuple_list);
}
/*
* deallocate memory occupied by links in a list
*/
int
rm_llistlink (struct linklist *list)
{
int link_count;
link_count = 0;
llhead (list);
do {
lldelete (list);
link_count++;
} while (list->listlength != 0);
return (link_count);
}
/*
* display edge tuple list
*/
void
show_etpl_list (struct linklist *list)
{
struct edge_tuple *cetpl;
int ie = 0, ne;
llhead (list);
if ((ne = ll_length (list)) != 0) { /* list not empty */
#ifdef DPRINT
gprintf (fpOut, "\n...list of %d edge tuples\n", ne);
#endif
do {
cetpl = (struct edge_tuple *) list->clp->item;
#ifdef DPRINT
gprintf (fpOut, " (%3d, %3d); status: %2d\n",
cetpl->cl, cetpl->cr, (int) cetpl->status);
if ((ie + 1) % 8 == 0) {
gprintf (fpOut, "\n");
ie = 0;
}
#endif
ie++;
} while (llnext (list) == True);
}
#ifdef DPRINT
else
gprintf (fpOut, " list empty\n");
#endif
}
/*
* display list (terse version)
*/
void
tshow_disk_list (struct linklist *list)
{
struct disk *cdsk;
int id = 0, nd;
llhead (list);
if ((nd = ll_length (list)) != 0) { /* list not empty */
gprintf (fpOut, "\n...list of %d disk centers\n", nd);
do {
cdsk = (struct disk *) list->clp->item;
gprintf (fpOut, " (%3d, %3d)", cdsk->ctr.x, cdsk->ctr.y);
if ((id + 1) % 8 == 0) {
gprintf (fpOut, "\n");
id = 0;
}
id++;
} while (llnext (list) == True);
}
else
gprintf (fpOut, " list empty\n");
}
/*
* display list
*/
void
show_disk_list (struct linklist *list)
{
struct disk *cdsk;
int id = 0, nd;
int ich;
llhead (list);
if ((nd = ll_length (list)) != 0) { /* list not empty */
gprintf (fpOut, "\n...list of %d disk structures\n", nd);
do {
cdsk = (struct disk *) list->clp->item;
gprintf (fpOut, "\n %3d chord(s) (r; cl, cr):\n", cdsk->nch);
for (ich = 0; ich < cdsk->nch; ich++) {
gprintf (fpOut, " (%3d; %3d, %3d)\n",
cdsk->chords[ich].r,
cdsk->chords[ich].cl,
cdsk->chords[ich].cr);
}
gprintf (fpOut, " center: (%3d, %3d)",
cdsk->ctr.x, cdsk->ctr.y);
gprintf (fpOut, "...radius: %3d", cdsk->rad);
gprintf (fpOut, "...status: %d\n", cdsk->Status);
} while (llnext (list) == True);
}
else
gprintf (fpOut, " list empty\n");
}
/*
* update initial estimate of disk diameter
*/
float
update_disk_dia (struct linklist *list)
{
struct disk *cdsk;
float odd, dd = (float) 0.0;
int nd;
odd = ddia;
dd = (float) 0.0;
nd = 0;
llhead (list);
do {
cdsk = (struct disk *) list->clp->item;
if (cdsk->nch > 1) {
dd += (float) 2.0 *cdsk->rad;
nd++;
}
} while (llnext (list) == True);
dd /= (float) nd;
if (fabs ((dd - ddia) / ddia) > 0.1)
return (dd);
else
return (odd);
}
/*
* gprintf() functions:
* write to std output and, if option WRITE_FILE set, to file wbuf (stream)
*/
void
gprintf (FILE * fpOut, char *fmt,...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
vprintf (fmt, arg_ptr);
if (WRITE_FILE == 1)
vfprintf (fpOut, fmt, arg_ptr);
va_end (arg_ptr);
}
/*
* error message
*/
void
exitmess (char *prompt, int status)
{
printf (prompt);
printf ("\n");
exit (status);
}
/*
* usage of routine
*/
void
usage (char *progname)
{
progname = last_bs (progname);
printf ("USAGE: %s inimg outimg [-t file] [-w file] [-s rad] [-n lines]\n", progname);
printf (" [-b] [-z pix] [-i imgtype] [-f len] [-L]\n");
printf ("\n%s determines, in an efficient manner, (estimates of) center positions\n", progname);
printf ("for circular objects by taking advantage of knwoledge about the\n");
printf ("circular object shape and object size, the latter in the form of\n");
printf ("known radius, R, or mean and variance of the distribution of radii.\n\n");
printf ("ARGUMENTS:\n");
printf (" inimg: input image filename (TIF)\n");
printf (" outimg: output image filename (TIF)\n\n");
printf ("OPTIONS:\n");
printf (" -t file: read test input from file fn ( .tpl) -s, -n options disabled\n");
printf (" -w file: write disk parameters to file fn.dsk\n");
printf (" -s rad: supply estimated (mean) disk radius (in pix)\n");
printf (" -n lines: supply min nof scan lines to sample each disk\n");
printf (" (must exceed %d)\n", NCH_MIN);
printf (" -b: do not display disk bdy\n");
printf (" -z pix: zero a border strip of width pix (default: %d)\n", BORDER_WIDTH);
printf (" -i str: specify image type, B(BINARY) (default) or G (GRAY) \n");
printf (" -f len: specify edge filter len, (odd)l (default: %d)\n", EDGE_FILTER_LEN);
printf (" -L: print Software License for this module\n");
exit (1);
}
void
main (int argc, char **argv)
{
Image *imgIn;
static char *inp_suffix =
{".tpl"}; /* default inp file suffix */
static char *tpl_type =
{".tpl"}; /* suffix for .pdt inp file */
static char *wsuffix =
{".dsk"}; /* suffix for output file name */
static char rdbuf[32], *buf = &rdbuf[0];
static char wrbuf[32], *wbuf = &wrbuf[0];
int ic, is;
int i_arg;
/* list structs */
struct linklist etll; /* list of edge tuples on current scan line */
int ne;
struct linklist dll; /* disk (linked) list */
#ifdef DEBUG
struct linklist *cdll; /* ptr to current dll */
#endif
int rm_link = 0;
int nds;
int nch;
/* AOI dimensions */
int ir, ir_base;
int nrw, ncl;
int jmin, imin, jmax, imax;
int left_x, right_x;
int strip_wdth = BORDER_WIDTH;
unsigned char *row;
int nf = EDGE_FILTER_LEN;
float *ef;
/*
* cmd line options ( see usage() ):
*/
static char *optstring = "t:w:s:n:z:bi:f:L";
/*
* parse command line
*/
optind = 3;
opterr = 1; /* give error messages */
if (argc < 2)
usage (argv[0]);
while ((i_arg = getopt (argc, argv, optstring)) != EOF) {
switch (i_arg) {
case 't':
printf ("...option %c: ", i_arg);
printf (" employ test data in file %s\n", buf = optarg);
TEST_INPUT = 1;
SCAN_IMG = 0;
UPDATE_DDIA = 0;
/* get size of data set from file */
if ((fpIn = fopen (buf, "r")) == NULL) {
printf ("\n...could not open input file\n");
exit (1);
}
/* strip file suffix and determine file type */
ic = 0;
while (*(buf + ic) != '.')
ic++;
for (is = 0; is < 4; is++)
*(inp_suffix + is) = *(buf + ic + is);
if (strncmp (inp_suffix, tpl_type, 4) == 0) {
INP_FILE_TYPE = TPL;
}
/* initialize size variables */
fetch_test_parms (fpIn, &ddia, &del_ir, &nch, &ir_base);
printf ("\n...test parameters retrieved: ");
printf (" disk dia: %f\n", ddia);
printf (" vert scan interval: %3d\n", del_ir);
printf (" corresp vert scan freq: %3d\n", nch);
R_DSK = 1;
VERT_SI = 1;
break;
case 'w':
printf ("...option %c: ", i_arg);
printf (" write disk data to file %s\n",
wbuf = optarg);
if ((fpOut = fopen (wbuf, "w")) == NULL) {
printf ("\n...cannot open %s for writing\n",
wbuf);
exit (1);
}
gprintf (fpOut, "...logging params in file %s\n", wbuf);
WRITE_FILE = 1;
break;
case 's':
printf ("...option %c: ", i_arg);
if (TEST_INPUT == 0) {
printf (" supply estimate of disk RADIUS: %f\n",
drad = (float) atof (optarg));
ddia = (float) 2.0 *drad;
R_DSK = 1;
if (VERT_SI == -1) {
nch = NCH_MIN;
printf (" -->default vert scan interval: %3d\n",
del_ir = F_TO_INT (drad) - 1);
}
}
else
printf (" TEST_INPUT -- option disabled\n");
break;
case 'n':
printf ("...option %c: ", i_arg);
if (TEST_INPUT == 0) {
printf (" set vert scan freq (min scan lns/disk): ");
printf ("nch = %d\n", nch = atoi (optarg));
if (R_DSK == -1) {
printf ("\n...must first supply estimate for (mean) disk radius\n");
exit (1);
}
printf (" -->implied vert scan interval: %3d\n",
del_ir = F_TO_INT (2.0 * drad / (float) nch));
if (nch * del_ir == F_TO_INT (ddia)) {
nch++;
printf (" -->adj vert scan freq: ");
printf (" max scan lns/dsk: %d\n", nch);
}
VERT_SI = 1;
}
else
printf (" TEST_INPUT -- option disabled\n");
break;
case 'b':
printf ("...option %c:", i_arg);
printf (" do not display disk boundaries\n");
DISPL_DISK_BDY = 0;
break;
case 'z':
printf ("...option %c:", i_arg);
printf (" zero a border strip of width %d\n",
strip_wdth = atoi (optarg));
ZB = 1;
break;
case 'i':
printf ("...option %c:", i_arg);
printf (" process %s image\n", IMG_TYPE = optarg);
if (strncmp (IMG_TYPE, "B", 1) == 0)
GRAY_LEV = 0;
else if (strncmp (IMG_TYPE, "G", 1) == 0)
GRAY_LEV = 1;
else
exitmess ("\n...unknown image type\n", 1);
break;
case 'f':
printf ("...option %c:", i_arg);
printf (" select edge filter length: ");
if ((nf = atoi (optarg)) % 2 == 0)
nf++;
printf ("%2d\n", nf);
break;
case 'L':
print_sos_lic ();
exit (0);
default:
printf ("\n...unknown condition encountered\n");
exit (1);
break;
}
}
if (R_DSK == -1) {
printf ("\n...must supply estimate for (mean) disk radius\n");
exit (1);
}
/*
* Read input image
*/
imgIn = ImageIn (argv[1]);
if (imgIn->bps == 8 && imgIn->spp == 3) {
printf ("Got RGB image!!!\nInput image must be Grayscale or B&W!!\n");
exit (1);
}
/*
* scan full frame (if AOI desired, supply boundary coordinates here)
*/
jmin = imin = 0;
jmax = imgIn->width;
imax = imgIn->height;
left_x = jmin;
right_x = jmax - 1;
ncl = jmax - jmin;
nrw = imax - imin;
#ifdef DEBUG
printf ("\n...ncl = %d, nrw = %d\n", ncl, nrw);
#endif
/*
* zero outer border of image (-->select one method)
*/
if (ZB == 1)
zero_border (imgIn, strip_wdth);
/*
* display parameter settings employing global printf function;
* open log file
*/
gprintf (fpOut, "\nparameter settings:\n");
gprintf (fpOut, " image type: %s\n", IMG_TYPE);
gprintf (fpOut, "vert scan interval (del_ir): %3d\n", del_ir);
gprintf (fpOut, " disk dia: %f\n", ddia);
/*
* memory allocation
*/
if ((row = (unsigned char *) calloc (ncl, sizeof (unsigned char))) == NULL)
exitmess ("mem alloc for row failed", 1);
if (TEST_INPUT == 1) {
gprintf (fpOut, "\n...process test data set in %s\n", buf);
}
else if (SCAN_IMG == 1) {
gprintf (fpOut, "\n...scan image\n");
if (GRAY_LEV == 1) {
if ((ef = (float *) calloc (nf, sizeof (float))) == NULL)
exitmess ("mem alloc for edge filter ef failed", 1);
fetch_edge_det (ef, nf);
}
}
/*
* initialize (empty) lists
*/
init_etll (&etll);
init_dll (&dll);
#ifdef DEBUG
cdll = &dll;
printf ("\n...structure parameters for disk list:\n");
printf ("sizeof(struct linklist): %d\n", sizeof (struct linklist));
printf (" item length: %d\n", cdll->itemlength);
printf (" cur list length: %d\n", cdll->listlength);
#endif
/*
* loop over (selected) rows
* for each row index: scan row, applying desired (1d) edge filter,
* and construct list of edge tuples;
*
* following completion of scan, update disk list by matching the
* ``runs'' of ON-pixels, given by the set of edge tuples {yl, yr},
* to the those entries in the disk list which remain ACTIVE:
* if no match is found, the respective disk entry in the list
* is closed (set to INACTIVE) and a new ACTIVE disk is opened
*
* matching is accmplished by examining ``symmetric'' overlap of
* runs in successive scans, as detailed below (see function
* update_dll() )
*/
if (TEST_INPUT == OFF)
ir_base = strip_wdth + 1;
ir = ir_base;
nds = 0; /* current number of disks */
do {
ne = 0; /* nof ON segm (edge tpls) per row */
init_etll (&etll);
/* scan image row */
if (TEST_INPUT == 1) {
ne = fetch_test_row (fpIn, &etll);
}
else if (SCAN_IMG == 1) {
getrow (row, ir, imgIn);
if (GRAY_LEV == 1)
edge_det (row, ncl, ef, nf);
#ifdef DPRINT
printf ("...about to encode row %d\n", ir);
#endif
ne = encode_row (row, ncl, &etll, GRAY_LEV);
}
#ifdef SHOW_ETLL
printf ("\n...row %3d: ", ir);
show_etpl_list (&etll);
#endif
if (ne > 0) {
nds += update_disk_list (ir, nch, &dll, &etll);
#ifdef CHECK_DLL
show_disk_list (&dll);
#endif
/* clear edge tuple list */
rm_link = rm_llistlink (&etll);
#ifdef DPRINT
printf ("...%d entries removed from edge tuple list\n", rm_link);
#endif
}
/* update initial estimate of disk diameter */
if ((UPDATE_DDIA == ON) && (ll_length (&dll) >= UPDATE_DL)) {
#ifdef DPRINT
printf ("\n...update init estimate of disk diameter\n");
#endif
ddia = update_disk_dia (&dll);
del_ir = F_TO_INT (ddia / (float) nch);
if (nch * del_ir >= (int) ddia)
nch++;
#ifdef DPRINT
printf (" -->new estimate of disk dia: %f\n", ddia);
printf (" -->new vert scan interval: %3d\n", del_ir);
printf (" -->new vert scan freq: %2d\n", nch);
#endif
UPDATE_DDIA = OFF;
}
ir += del_ir;
} while (ir < nrw - (ir_base + 1));
/*
* display results
*/
#ifdef DPRINT
printf ("\n...done with construction of disk list:\n");
if (DISP_MODE == VERBOSE)
show_disk_list (&dll);
else if (DISP_MODE == TERSE)
tshow_disk_list (&dll);
#endif
/*
* overlay disk contours onto original image
*/
if (DISPL_DISK_BDY == ON) {
displ_disk_contours (&dll, imgIn, GRAY);
}
/*
* deallocate memory assigned to lists
*/
#ifdef DPRINT
printf ("\n...deallocating memory for linked lists\n");
printf (" disks:\n");
#endif
rm_link = rm_llistlink (&dll);
#ifdef DBG_MEM
printf (" %d links removed from disk list\n", rm_link);
#endif
/*
* deallocate memory and close files
*/
free (row);
if (GRAY_LEV == 1)
free (ef);
if (TEST_INPUT == 1)
fclose (fpIn);
if (WRITE_FILE == 1)
fclose (fpOut);
/*
* Write the output image
*/
printf ("...writing image file %s\n", argv[2]);
ImageOut (argv[2], imgIn);
}